package com.jutixueyuan.service.impl;

import com.jutixueyuan.pojo.House;
import com.jutixueyuan.pojo.HouseES;
import com.jutixueyuan.pojo.LivegoodsResult;
import com.jutixueyuan.service.SearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Value("${livegoods.fastdfs.nginx}")
    private String livegoodsFastdfsNginx;
    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 初始化ElasticSearch 数据要做两件事情
     * 1，查询出MongoDB数据库数据
     * 2，将数据封装成HoustES对象批量插入到ES中去
     *  适合少量数据
     *
     * @return
     */
    @Override
    public Long init() {
        //获取当前时间
        long begin = System.currentTimeMillis();

        //1.查询出MongoDB数据库数据
        List<House> houses = mongoTemplate.findAll(House.class);


        /*id: Math.random().toString().slice(2),
                title: "豪宅 · 使馆壹号院4居室-南",
                houseType: "17/19层| 4室1厅 - 273.97 ㎡",
                price: "<h3>130000</h3>",
                rentType: "整租",
                img: "http://iwenwiki.com/api/livable/search/1.jpg"*/


       /* rentType: "合租",
                houseType: "100 ㎡",
                info: {
                    years: "2002年",
                    type: "3室1厅",
                    level: "4/7层",
                    style: "简装",
                    orientation: "朝南"
        },*/

        //创建List<IndexQuery>集合用于封装数据
        List<IndexQuery> indexQueryList = new ArrayList<>();
        //循环Mongodb的House数据并封装到HouseES对象中去
        for (House house : houses) {
            //创建HouseES对象
            HouseES houseES = new HouseES();
            houseES.setId(house.getId());
            houseES.setTitle(house.getTitle());
            houseES.setPrice("<h3>"+house.getPrice()+"</h3>");
            houseES.setCity(house.getCity());
            houseES.setRentType(house.getRentType());
            houseES.setImg(house.getImgs().get(0));
           //房屋类型的数据组装 ： houseType: "17/19层| 4室1厅 - 273.97 ㎡",
            String level = house.getInfo().get("level");//4/7层
            String type = house.getInfo().get("type");//4室1厅
            houseES.setHouseType(level+" | "+type+" - "+house.getHouseType());

            //创建IndexQuery用于封装HouseEs

            IndexQuery indexQuery = new IndexQuery();
            indexQuery.setObject(houseES);

            //将IndexQuery添加到集合中
            indexQueryList.add(indexQuery);

        }

        //创建索引，设置Mapping(自动创建，所以必须要先创建索引，Pojo上面设置对应的映射：分词，类型才会生效)
        elasticsearchRestTemplate.createIndex(HouseES.class);
        elasticsearchRestTemplate.putMapping(HouseES.class);


        //批量插入
        elasticsearchRestTemplate.bulkIndex(indexQueryList);


        return System.currentTimeMillis()-begin;
    }

    /**
     * 根据条件高亮搜索
     *
     * 1，城市条件
     *     和条件2 must（AND） 关系
     * 2，关键字去三个Field中搜索
     *      title :房屋名称
     *      houseType :房屋类型
     *      rentType：出租类型
     *      条件2中的三个条件是 ： should(or)关系
     * 3，高亮field
     *    titlle,houseType
     *
     * 4，分页：前端项目用户是不会主动给size，我们默认给一个每页条数size即可
     *      10 条
     *
     *
     *
     * @param city
     * @param keyword
     * @param page
     * @return
     */
    @Override
    public LivegoodsResult search(String city, String keyword, Integer page,Integer size) {



        //NativeSearchQueryBuilder对象，用于构建SearchQuery
        //在构建对象中设置高亮，条件，分页
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();


        //设置城市条件
        MatchQueryBuilder cityQueryBuilder = QueryBuilders.matchQuery("city", city);


        //创建关键字搜索的三个Field 或者条件
        BoolQueryBuilder keywordBoolQueryBuilder = QueryBuilders.boolQuery();
        keywordBoolQueryBuilder.should(QueryBuilders.matchQuery("title",keyword));
        keywordBoolQueryBuilder.should(QueryBuilders.matchQuery("houseType",keyword));
        keywordBoolQueryBuilder.should(QueryBuilders.matchQuery("rentType",keyword));


        //创建新的BoolQueryBuilder 把 城市条件：cityQueryBuilder 和三个关键字搜索条件再作为并且关系 ：
        BoolQueryBuilder totalBoolQueryBuilder = QueryBuilders.boolQuery();
        totalBoolQueryBuilder.must(cityQueryBuilder);
        totalBoolQueryBuilder.must(keywordBoolQueryBuilder);


        //设置searchQueryBuilder的整体条件
        searchQueryBuilder.withQuery(totalBoolQueryBuilder);


        //分页
        searchQueryBuilder.withPageable(PageRequest.of(page,size));



        //创建高亮对象
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.numOfFragments(5);
        highlightBuilder.fragmentSize(20);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");

        searchQueryBuilder.withHighlightBuilder(highlightBuilder);




        //构建SearchQuery对象:
        SearchQuery searchQuery = searchQueryBuilder.build();

        //执行查询
        AggregatedPage<HouseES> aggregatedPage = elasticsearchRestTemplate.queryForPage(searchQuery, HouseES.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

                //获取搜索结果命中数据
                SearchHit[] hits = response.getHits().getHits();

                List<HouseES> houseESList = new ArrayList<>();
                //循环搜索的数据封装到HouseES对象
                for (SearchHit hit : hits) {
                    HouseES houseES = new HouseES();
                    houseES.setId(hit.getId());
                    //获取源数据
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    houseES.setHouseType(sourceAsMap.get("houseType").toString());
                    houseES.setPrice(sourceAsMap.get("price").toString());
                    houseES.setImg(livegoodsFastdfsNginx+sourceAsMap.get("img").toString());
                    houseES.setCity(sourceAsMap.get("city").toString());
                    houseES.setRentType(sourceAsMap.get("rentType").toString());
                    //获取高亮
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    if(highlightFields.containsKey("title")){
                        HighlightField highlightField = highlightFields.get("title");
                        Text fragment = highlightField.getFragments()[0];
                        String higlightTitle = fragment.toString();

                        //将高亮数据设置给houseES 的title
                        houseES.setTitle(higlightTitle);
                    }
                    houseESList.add(houseES);
                }
                //获取总数
                long totalHits = response.getHits().getTotalHits();


                return new AggregatedPageImpl(houseESList,  pageable,totalHits);
            }

            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
                return null;
            }
        });

        //获取结果
        List<HouseES> content = aggregatedPage.getContent();

        //获取总记录
        long totalElements = aggregatedPage.getTotalElements();

        //获取总页数
        int totalPages = aggregatedPage.getTotalPages();


        LivegoodsResult livegoodsResult = LivegoodsResult.ok(content);

        //判断是否还有数据（外界传递过来的页面小于总页数，说明还有数据）
        if(page+1<totalPages){
            livegoodsResult.setHasMore(true);
        }

        return livegoodsResult;
    }
}
